chore(master): release 0.23.0 - #433
Conversation
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the full generated release-please diff for the 0.23.0 desktop release, checked against the actual commit range since v0.22.0 and against the release contracts in docs/RELEASE.md.
- Manifest bump —
.release-please-manifest.json"."moves0.22.0→0.23.0. - Desktop version bump —
apps/desktop/package.jsonversion→0.23.0, written through the config'sextra-filesjsonpath$.version. - Changelog entry — a new
## [0.23.0]section listingfeat(config): generate branded desktop and mobile identity(#428,a50ea0f).
Three things I checked rather than assumed:
- The range since
v0.22.0(6d8f9dac) is exactly two commits — #427 (test:) and #428 (feat:).test:has no default changelog section, so its omission is correct, and a minor bump is right for afeatunderbump-minor-pre-majoron 0.x. - Invariant 5 holds:
apps/desktop/package.jsonreads0.23.0and the tag will bev0.23.0, which is whatbuild-desktop.yml's "Assert package version matches release tag" step compares. - #428's brand work does not re-brand this release.
assertStagedConfigMatchesGeneratedmarks a build branded only when the config bundle'sbrandId !== 'linkcode', so the default release keepselectron-builder.yml'sappId: com.arcboxlabs.linkcode.desktop,productName: LinkCode, and the base publish feed — artifact names and updater feed continuity for 0.22.0 clients are unaffected.
ℹ️ This is the first tag cut since the desktop packaging path was reworked
Nothing in this diff is wrong, but v0.23.0 is the first release to run #428's reworked packaging scripts for real. Signed builds set LINKCODE_REQUIRE_CONFIG_BUNDLE=1, so the new hard-fail assertStagedConfigMatchesGenerated gate executes on a real signed release for the first time on this tag; today it has unit coverage only. A failure there aborts packaging after the tag already exists, which is the awkward state to unwind.
Technical details
# First signed build on the reworked desktop packaging path
## Affected sites
- `apps/desktop/scripts/package-config.mts` — `assertStagedConfigMatchesGenerated` is a new hard-fail gate comparing `apps/desktop/generated` against `apps/desktop/out/config`; it throws rather than degrading.
- `apps/desktop/scripts/package-app.mts` — calls that gate before every pack, and additionally refuses `-c.appId` / `-c.productName` / `-c.protocols` passthrough overrides on branded builds.
- `.github/workflows/build-desktop.yml` — sets `LINKCODE_REQUIRE_CONFIG_BUNDLE: 1` whenever `sign` is true, so the config-bundle staging path is mandatory on exactly this release.
## Required outcome
- Confidence that a signed desktop package succeeds end-to-end on `a259c3f` before the `v0.23.0` tag is pushed.
## Suggested approach
- Dispatch `release-desktop.yml` with `dry_run: true` against this branch or the prospective merge SHA — it builds and signs but publishes nothing, which is precisely the rehearsal this situation calls for.
## Open questions for the human
- Does the configured `CONFIG_RELEASE_MANIFEST_DESKTOP` for this release resolve to `brandId: linkcode`? That is the value that keeps `branded` false and preserves `appId` / `productName` / the publish feed; it is an org-level repo variable and not verifiable from the repo.Claude Opus | 𝕏
lucas77778
left a comment
There was a problem hiding this comment.
Reviewed the complete release-please diff and release contracts. Verified the manifest and desktop package both advance from 0.22.0 to 0.23.0, the changelog accurately reflects the feat commit since v0.22.0 while correctly omitting the test-only commit, and the minor bump matches bump-minor-pre-major. The tag/package version contract remains aligned, and all current CI and CodeQL checks pass. No blocking findings.
a259c3f to
9540bb5
Compare
There was a problem hiding this comment.
Important
The regenerated changelog lists #426 as a plain feature, but it moves MIN_COMPATIBLE_WIRE_VERSION from 68 to 76 — this release refuses every peer built before it, and nothing in the notes says so.
Reviewed changes — the delta since the prior pullfrog review of a259c3f. release-please regenerated the release commit on top of the new master (084933c), so a second feature joined the release; the version itself did not move.
- Added #426 to the 0.23.0 changelog —
feat(providers,schema,engine,workbench): retire the default model(084933c) now sits under### Featuresalongside #428. - Re-dated the release section —
2026-08-08→2026-08-10. - Left the version untouched —
.release-please-manifest.jsonandapps/desktop/package.jsonstill read0.23.0. Both entries arefeatandbump-minor-pre-major: truemakes even a breaking change a minor bump on 0.x, so the minor is correct either way and Invariant 5'sv0.23.0tag pairing still holds.
⚠ Nothing coordinates the peers this release locks out
MIN_COMPATIBLE_WIRE_VERSION now equals WIRE_PROTOCOL_VERSION (76), so there is zero backward tolerance in either direction, and the desktop app does not always own the daemon it talks to: it stands down for an externally-running daemon and dials whatever runtime.json advertises, and settings.daemonUrl bypasses spawning entirely. apps/mobile is shipped separately to TestFlight / Play internal and is outside this release train, so a mobile build predating #426 stops connecting to a 0.23.0 daemon at the 5s handshake timeout with nothing on screen naming the cause.
Technical details
# v0.23.0 severs the wire with pre-#426 peers, with no announcement and no diagnosis
## Affected sites
- `packages/foundation/schema/src/wire/message.ts:12,16` — #426 moved `WIRE_PROTOCOL_VERSION` 73 → 76 and `MIN_COMPATIBLE_WIRE_VERSION` 68 → 76. The floor jumped eight versions in one release and now sits at the current version, so no older peer is accepted.
- `apps/desktop/src/main/daemon-supervisor.ts:29,124-127` — on `DAEMON_EXIT_ALREADY_RUNNING` the supervisor stands down rather than replacing the incumbent, and `isDaemonManaged()` is false whenever `settings.daemonUrl` is set. Combined with `apps/desktop/src/main/daemon-discovery.ts:14-24` (`settings.daemonUrl ?? discoverRuntimeUrl() ?? default`), a 0.23.0 renderer can end up dialing a daemon it did not build.
- `.github/workflows/build-mobile.yml:6-13,38-41` — mobile ships on its own `workflow_dispatch(submit)` to TestFlight and Play internal testing, decoupled from `release-please`. A build cut before `084933c` speaks wire ≤ 75.
- `packages/client/core/src/client.ts:190-199,651-655` — the diagnosis only exists in one direction. New client vs old host: `pong` arrives and `wireIncompatibility` returns `host speaks wire v73, older than the v76 this build needs`, but `packages/client/workbench/src/app/connection-state.tsx` renders only the generic `error`/`errorManaged` copy, so the string is log-only. Old client vs new host: `parseWireMessage` refuses the sub-floor `ping` before anything answers, so no `pong` ever comes and the connection dies at `HANDSHAKE_TIMEOUT_MS` — the CODE-447 shape, where only an out-of-band probe can name the cause.
- `apps/daemon/src/config.ts` (`withEnabledAccounts`, `withPickedModels`) and `apps/daemon/drizzle/0009_add_session_run_pin.sql` — legacy config fields are folded forward and dropped on the next write, and the migration has no down step. Reinstalling 0.22.0 after 0.23.0 does not restore the previous model / default-account state.
## Required outcome
- A decision, before the tag is pushed, on whether anything outside this release train has to ship alongside `v0.23.0` — and release notes that let a user whose client stops connecting understand why.
## Open questions for the human
- Is a mobile build predating `084933c` currently in testers' hands via TestFlight or Play internal? That is not verifiable from this repo, and it is the difference between "no field exposure" and "testers hit an unexplained 5s timeout the moment their daemon updates".
- Is the desktop side worth threading `wireIncompatibility`'s message into the connection screen as a follow-up issue? The string already exists and is already correct; only desktop discards it. Mobile renders it, but in the direction that cannot fire here.Claude Opus | 𝕏
| ## [0.23.0](https://github.com/arcboxlabs/linkcode/compare/v0.22.0...v0.23.0) (2026-08-10) | ||
|
|
||
|
|
||
| ### Features |
There was a problem hiding this comment.
#426 is listed as a plain feature, but it removes ProviderConfig.activeAccountId/model, drops config.account.create-and-bind, and moves both wire versions to 76 — its own squashed body says so (feat(schema,engine,daemon)!: … BREAKING: … both wire versions move to 76). release-please could not see it: the squash subject carries no !, and the footer is spelled BREAKING:, which is not one of conventional-commits' BREAKING CHANGE / BREAKING-CHANGE note keywords.
The version is unaffected (bump-minor-pre-major makes a breaking change a minor bump on 0.x anyway), but these notes are what finalize-releases.yml turns into the draft release, so the break reaches users unannounced. Adding the section by hand here is the only fix left for this tag — the commit convention is the fix for the next one.
| ### Features | |
| ### ⚠ BREAKING CHANGES | |
| * **schema,engine,daemon:** `ProviderConfig` loses `activeAccountId` and `model`, `config.account.create-and-bind` is gone, and both wire versions move to 76 — a client or daemon below wire v76 can no longer connect ([#426](https://github.com/arcboxlabs/linkcode/issues/426)) | |
| ### Features |
9540bb5 to
6a97f37
Compare
|
Your Claude subscription has hit its usage limit. It resets at Aug 15, 9pm (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
6a97f37 to
b595a9b
Compare
|
Your Claude subscription has hit its usage limit. It resets at Aug 15, 9pm (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
b595a9b to
17947bf
Compare
|
Your Claude subscription has hit its usage limit. It resets at Aug 15, 9pm (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
17947bf to
0e78124
Compare
|
Your Claude subscription has hit its usage limit. It resets at Aug 15, 9pm (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
0e78124 to
ac176a9
Compare
|
Your Claude subscription has hit its usage limit. It resets at 9pm (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
ac176a9 to
a9af747
Compare
There was a problem hiding this comment.
Important
The release grew from 3 changelog entries to 23 since the last review, but the one thing a user needs to be told is still missing: #426 is listed as a plain feature while it raises the wire floor above everything v0.22.0 speaks. The existing thread on apps/desktop/CHANGELOG.md:6 still anchors correctly and its suggestion still applies verbatim, so I have not re-posted it.
Reviewed changes — the delta since the prior pullfrog review of 9540bb5. release-please regenerated the release commit twice on top of a much larger master; the version did not move.
- Grew the 0.23.0 changelog from 2 entries to 23 — 10 more features and a new
### Bug Fixessection with 11. Almost all of it is release-pipeline work (artifact provenance binding and a verification CLI, isolated brand matrix, protected release environment and input validation, store-configuration compliance, cross-repository config publication); the two user-facing additions are #430 and #431. - Re-dated the release section —
2026-08-10→2026-08-16. - Left the version at
0.23.0— correct. No commit subject in the range carries!, every entry isfeat/fix, andbump-minor-pre-major: truekeeps 0.x on a minor regardless.
Four things I checked rather than assumed, all clean:
- The changelog matches the commit range exactly.
6d8f9dac..e79cbf3cis 31 commits; all 12featand 11fixare present. The single omitted feature,f1c36925 feat(mobile): bind branded release destinations, touches onlyapps/mobile/**, whichrelease-please-config.jsonexclude-pathsexcludes — correct, not a miss. The sixtest:/docs:/ci:commits have no default changelog section. - Auto-update continuity survives the brand-matrix rework. Re-checked against the current tree, not just #428: the publish URL (
electron-builder.yml:78-83),artifactNametemplate (:89), defaultappId/productName(:13-14), thelatest*.ymlmanifest names (package-app.mts:157-161), and the R2desktop/prefix (release-desktop.yml:115) are all unchanged. Every new brand path is gated onbranded === true, which the defaultlinkcodebuild never sets. A 0.22.0 install will find and apply this update. - The
'skipped'escape-hatch hazard inrelease-brand-matrix.ymlis closed. Thedesktopjob (:488) accepts'skipped'only conjoined with!inputs.sign, and that branch passessign: falsedownstream; a force-skip caused by a failedrelease-environment-preflightonly occurs wheninputs.signis true, which falsifies the guard.mobile(:633) demands'success'. The file isworkflow_dispatch-only anyway. - The tag path is structurally intact.
release-desktop.ymlis byte-identical across this range, so itsenvironment: releasealready cleared a tag push at v0.22.0 — the deployment-branch policy demonstrably permitsv*.*.*. Remaining dependencies are org-level only (vars.CONFIG_*,secrets.BOT_APP_*, signing and R2 secrets).
⚠️ The first-signed-build risk flagged at a259c3f is now about twenty times larger
I raised this when the release contained #428 alone; restating it because the scope changed materially rather than to repeat it. v0.23.0 now carries roughly twenty release-pipeline commits whose gates have never run on a real signed release — provenance binding, store-configuration compliance, protected-input validation, and cross-repository config publication, on top of the packaging rework. Most were pushed straight to master with no PR number, so they carry no independent review, and finalize-releases.yml cuts the tag automatically once CI passes, which means any failure surfaces after the tag exists.
Technical details
# v0.23.0 is the first real exercise of a substantially rebuilt release pipeline
## Affected sites
- `.github/workflows/build-desktop.yml` — sets `LINKCODE_REQUIRE_CONFIG_BUNDLE: 1` whenever `sign` is true, so the hard-fail `assertStagedConfigMatchesGenerated` gate and the `render-config` job both run for real on this tag; unit coverage only today.
- `.github/workflows/finalize-releases.yml` — creates the `v0.23.0` tag and draft release on CI success alone. CI does not exercise signing, provenance binding, or R2 publication, so the tag is created before any of those gates has been tried.
- Commits in `6d8f9dac..e79cbf3c` with no PR number — `e1d037fa`, `8a19b059`, `7c984113`, `f91fcbe0`, `7c32c925`, `5d6f81bd`, `42a825bd`, `047c5b9b`, `cc51e0bf`, `44bcf678`, `7a1ec14b`, `dd296991`, `8bf90cbe`, `856c199e`, `5f8ed5e7`, `547ff17e`, `e79cbf3c`.
## Required outcome
- Evidence that a signed desktop package builds, binds provenance, and passes the config-publication gates end-to-end on this SHA *before* the `v0.23.0` tag is pushed.
## Suggested approach
- Dispatch `release-desktop.yml` with `dry_run: true` against the prospective merge SHA. It builds and signs but publishes nothing, which is exactly the rehearsal this situation calls for — and it is now the only thing standing between an untried pipeline and an already-created tag.
## Open questions for the human
- Are the org-level `release`-environment values current for this cycle — `CONFIG_PUBLISHER_REPO`, `CONFIG_SOURCE_REPO`, `CONFIG_RELEASE_REVISION`, `CONFIG_RELEASE_KEYRINGS`, `CONFIG_RELEASE_MANIFEST_DESKTOP`? Several are consumed by jobs that did not exist at v0.22.0, so a stale or absent value fails a step that has never run on a tag before.ℹ️ Nitpicks
require observed config publicationappears twice in### Bug Fixes(5f8ed5eand7677211/ #451) — two commits, identical subject, so release-please emitted both. Harmless, but worth collapsing if the notes are hand-edited anyway.- 21 of the 23 entries are
release:- orconfig:-scoped internal pipeline work that no user can act on, andfinalize-releases.ymlturns these notes into the draft GitHub release. release-please filters by type, not scope, so the only lever is committing this kind of plumbing aschore(release):in future — nothing to change on this tag beyond an optional trim.
Claude Opus | 𝕏
a9af747 to
e27270a
Compare
|
Your Claude subscription has hit its usage limit. It resets at 3:20pm (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
e27270a to
240099b
Compare
|
Your Claude subscription has hit its usage limit. It resets at 3:20pm (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
240099b to
db884a1
Compare
db884a1 to
b42ddf7
Compare
|
Your Claude subscription has hit its usage limit. It resets at 4:10am (UTC). Re-trigger Pullfrog after the reset, or add an Add repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
|
🤖 Created releases: 🌻 |

🤖 I have created a release beep boop
0.23.0 (2026-08-17)
Features
Bug Fixes
This PR was generated with Release Please. See documentation.